#pragma once

#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "InetAddr.hpp"
#include "HandleConnection.hpp"
#include "Connection.hpp"

using namespace Socket_ns;

class Listener
{
public:
    Listener(int port, HandleConnection& hc)
        : _port(port), _listenSock(std::make_unique<TCPSocket>()), _hc(hc)
    {
        InetAddr serverAddr("0", _port);
        _listenSock->BuildListenSocket(serverAddr);
    }

    int SockFd()
    {
        return _listenSock->SockFd();
    }

    // 监听套接字可能接收到多个客户端的请求，因此需要循环读取（listenSock被设置为了非阻塞，因此不会阻塞）
    void Accept(Connection *con)
    {
        while (true)
        {
            InetAddr clientAddr;
            int errcode = 0;
            // 因为采用循环处理请求，在处理完时再进行Accepter会出错，根据错误码可以帮助我们得知处理完毕
            int fd = _listenSock->Accepter(&clientAddr, &errcode); 
            if (fd >= 0)
            {
                // LOG(DEBUG, "accepter a new link: %d", fd);
                // 添加新连接
                con->_R->AddConnection(fd, EPOLLIN | EPOLLET, 
                std::bind(&HandleConnection::HandleReader, &_hc, std::placeholders::_1),  
                std::bind(&HandleConnection::HandleWriter, &_hc, std::placeholders::_1), 
                std::bind(&HandleConnection::HandleExcepter, &_hc, std::placeholders::_1),
                clientAddr);
            }
            else
            {
                if(errcode == EWOULDBLOCK || errcode == EAGAIN) // 已经处理完所有请求
                {
                    LOG(INFO, "accepter all link!");
                    break;
                }
                else if(errcode == EINTR) // 如果在处理请求时，遇到信号中断，则需要继续处理请求
                {
                    LOG(INFO, "accepter interrupt by signal");
                    continue;
                }
                else // 处理请求出错
                {
                    LOG(WARNING, "accepter error!");
                    break;
                }
            }
        }
    }

    ~Listener()
    {
        _listenSock->Close();
    }

private:
    std::unique_ptr<Socket> _listenSock;
    uint16_t _port;
    HandleConnection &_hc; // 用于监听套接字获取到的普通fd的读写操作
};